package com.wewins.fota.sys.task;

import ch.qos.logback.core.ContextBase;
import ch.qos.logback.core.rolling.helper.CompressionMode;
import ch.qos.logback.core.rolling.helper.Compressor;
import ch.qos.logback.core.rolling.helper.FileFilterUtil;
import com.alibaba.fastjson.JSONObject;
import com.wewins.fota.bean.bo.ServerInfo;
import com.wewins.fota.bean.bo.SysLog;
import com.wewins.fota.constant.FotaConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * 对于非正常关闭的情况下，如果日志文件正在压缩可能导致压缩不完全，该service处理这类情况；
 * logback会生成对应的tmp文件，将tmp文件重新写入gz
 * service仅处理gzip的压缩方式，其他压缩方式需额外处理。
 */
@Slf4j
@Component
public class LogInitTask {
    private Compressor compressor = new Compressor(CompressionMode.GZ);

    @Value("${logging.file.path}")
    private String logPath;

    @Value("${logging.file.archive}")
    private String archivePath;

    private final ServerInfo serverInfo;
    private final RedisTemplate<String, String> template;

    @Autowired
    public LogInitTask(ServerInfo serverInfo, RedisTemplate<String, String> template) {
        this.serverInfo = serverInfo;
        this.template = template;
        this.compressor.setContext(new ContextBase());
    }

    @PostConstruct
    public void initLog() {
        Runnable runnable = () -> {
            log.info("start to init log System.");
            dealGzCompress();
            logMonitor();
        };
        Thread thread = new Thread(runnable, "fota-init-log");
        thread.start();
    }

    @Scheduled(cron = "0 */5 * * * ?")
    public void logMonitor() {
        List<SysLog> logInfos = getFileListInfo(new File(logPath), ".*\\.log");
        List<SysLog> gzInfos = getFileListInfo(new File(archivePath), ".*\\.gz");
        logInfos.addAll(gzInfos);
        cache2Redis(logInfos);
    }

    private void cache2Redis(List<SysLog> logInfos) {
        template.delete(FotaConst.REDIS_KEY_SYS_LOG + ":" + serverInfo.getKey());
        logInfos.forEach(item -> template.opsForHash().put(FotaConst.REDIS_KEY_SYS_LOG + ":" + item.getOwner(), item.getName(), JSONObject.toJSONString(item)));
    }

    private List<SysLog> getFileListInfo(File file, String stemRegex) {
        File[] files = FileFilterUtil.filesInFolderMatchingStemRegex(file, stemRegex);
        List<SysLog> logs = new ArrayList<>();
        for (File tmp : files) {
            logs.add(SysLog.builder()
                    .name(tmp.getName())
                    .owner(serverInfo.getKey())
                    .fullPath(tmp.getAbsolutePath())
                    .lastUpdate(tmp.lastModified())
                    .size(tmp.length())
                    .build());
        }
        return logs;
    }

    private void dealGzCompress() {
        File archiveDir = new File(archivePath);
        File[] files = FileFilterUtil.filesInFolderMatchingStemRegex(archiveDir, ".*\\.tmp");
        if (files == null || files.length == 0) {
            return;
        }

        for (File file : files) {
            String name = file.getName();
            String gzName = name.substring(0, name.indexOf(".log") + 4) + ".gz";
            File oldGzFile = new File(archiveDir, gzName);
            FileUtils.deleteQuietly(oldGzFile);
            compressor.compress(file.getAbsolutePath(), oldGzFile.getAbsolutePath(), null);
        }
    }
}
